// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "CaeClientNotifyTool.h"
#include <pthread.h>
#include <vector>
#include "../hwsecure/securec.h"
#include "../cae_common/CaeAppCtrlCmdUtils.h"
#include "../cae_common/CaeLogConfig.h"
#include "../cae_common/CaeMsgCode.h"
#include "../cae_dataPipe/CaeMsgHead.h"
#include "CaeSocketAgent.h"

using namespace std;

CaeClientNotifyTool &CaeClientNotifyTool::GetInstance()
{
    static CaeClientNotifyTool instance;
    return instance;
}

CaeClientNotifyTool::CaeClientNotifyTool() = default;

CaeClientNotifyTool::~CaeClientNotifyTool() = default;

bool CaeClientNotifyTool::SendMsgToClient(const int msg, const std::weak_ptr<CaeSocketAgent>& socketAgent)
{
    map<string, string> params = CaeAppCtrlCmdUtils::GenerateNotifyParams(msg);
    string cmdStr = CaeAppCtrlCmdUtils::GenerateCommand(params);
    size_t strLen = cmdStr.size();

    if (msg == CAE_TRAIL_PLAY_TIMEOUT) {
        m_trailTimeout = true;
    }
    if (msg == CAE_NOTOUCH_TIMEOUT) {
        m_touchTimeout = true;
    }

    std::shared_ptr<uint8_t> responseBuff;
    responseBuff = std::shared_ptr<uint8_t>(new uint8_t[strLen + sizeof(STREAM_MSG_HEAD)], std::default_delete<uint8_t[]>());
    if (responseBuff == nullptr) {
        LOGE("Failed to malloc responseBuff.");
        return false;
    }

    StreamMsgHead msgHead {};
    msgHead.type = CAE_MSG_CONTROL;
    msgHead.SetPayloadSize(static_cast<uint32_t>(strLen));
    msgHead.magicWord = CAE_STREAM_DELIMITER_MAGICWORD;
    msgHead.checksum = static_cast<uint8_t>(GET_CAE_CHECKSUM(CAE_MSG_CONTROL));
    if (EOK != memcpy_s(responseBuff.get(), sizeof(STREAM_MSG_HEAD), &msgHead, sizeof(STREAM_MSG_HEAD))) {
        LOGE("Failed to memcpy msgHead to responseBuff.");
        responseBuff = nullptr;
        return false;
    }

    if (EOK != memcpy_s(responseBuff.get() + sizeof(STREAM_MSG_HEAD), strLen, cmdStr.c_str(), strLen)) {
        LOGE("Failed to memcpy cmdStr to responseBuff.");
        responseBuff = nullptr;
        return false;
    }

    int bytes = 0;
    if (!socketAgent.expired()) {
        std::shared_ptr<CaeSocketAgent> weakSocketAgent = socketAgent.lock();
        bytes = weakSocketAgent->Send(responseBuff.get(), strLen + sizeof(STREAM_MSG_HEAD));
    }
    responseBuff = nullptr;
    if (bytes <= 0) {
        LOGE("Failed to Send msg %s to client.", cmdStr.c_str());
        return false;
    } else {
        LOGD("Send msg to client: type: %c, Send %s to client: %d.", msgHead.type, cmdStr.c_str(), bytes);
        return true;
    }
}

bool CaeClientNotifyTool::ResponseCmdToClient(std::map<std::string, std::string> params, const std::weak_ptr<CaeSocketAgent> &socketAgent)
{
    string cmdStr = CaeAppCtrlCmdUtils::GenerateCommand(std::move(params));
    size_t strLen = cmdStr.size();
    std::shared_ptr<uint8_t> responseBuff;
    responseBuff = std::shared_ptr<uint8_t>(new uint8_t[strLen + sizeof(STREAM_MSG_HEAD)], std::default_delete<uint8_t[]>());
    if (responseBuff == nullptr) {
        LOGE("Failed to malloc control start app response.");
        return false;
    }

    if (!socketAgent.expired()) {
        std::shared_ptr<CaeSocketAgent> weakSocketAgent = socketAgent.lock();
        if (!weakSocketAgent->IsSocketValid()) {
            LOGE("CaeClientDataHandleThread socket is invalid.");
            responseBuff = nullptr;
            return false;
        }
    }

    StreamMsgHead msgHead {};
    uint8_t  msgType = CAE_MSG_CONTROL;
    msgHead.type = msgType;
    msgHead.SetPayloadSize((uint32_t) strLen);
    msgHead.magicWord = CAE_STREAM_DELIMITER_MAGICWORD;
    msgHead.checksum = (uint8_t) GET_CAE_CHECKSUM(msgType);
    if (memcpy_s(responseBuff.get(), sizeof(STREAM_MSG_HEAD), &msgHead, sizeof(STREAM_MSG_HEAD)) != EOK) {
        LOGE("Failed to memcpy msg head.");
        responseBuff = nullptr;
        return false;
    }

    if (memcpy_s(responseBuff.get() + sizeof(STREAM_MSG_HEAD), strLen, cmdStr.data(), strLen) != EOK) {
        LOGE("Failed to memcpy msg data.");
        responseBuff = nullptr;
        return false;
    }

    int bytes = 0;
    if (!socketAgent.expired()) {
        std::shared_ptr<CaeSocketAgent> weakSocketAgent = socketAgent.lock();
        bytes = weakSocketAgent->Send(responseBuff.get(), static_cast<int>(strLen + sizeof(STREAM_MSG_HEAD)));
    }

    responseBuff = nullptr;
    if (bytes <= 0) {
        LOGE("CAE send %s to client error.", cmdStr.c_str());
        return false;
    }
    return true;
}

void CaeClientNotifyTool::ResetTrailTimeout()
{
    m_trailTimeout = false;
}

bool CaeClientNotifyTool::GetTrailTimeoutState() const
{
    return m_trailTimeout;
}

void CaeClientNotifyTool::ResetTouchTimeout()
{
    m_touchTimeout = false;
}

bool CaeClientNotifyTool::GetTouchTimeoutState() const
{
    return m_touchTimeout;
}